Repository navigation
feat(agent): require wallet-signed challenge session for chat and history (#1314) - #1316
phatloi0010 wants to merge 1 commit into
Conversation
|
@phatloi0010 is attempting to deploy a commit to the miracle656's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
In frontend/wallet/app/agent/page.tsx and frontend/wallet/app/api/agent/route.ts, gating POST and GET chat/history handlers behind a wallet-signed challenge session closes the unauthenticated history read vector.
Quick question on session lifecycle: when a user switches the active Freighter public key in the browser without reloading the tab, does page.tsx immediately clear sessionToken and prompt a fresh challenge signature for the new address?
| import { requirePasskey } from '@/lib/passkeyAuth' | ||
| import { isPasskeyCancellation } from '@/lib/passkeyOutcome' | ||
| import { walletLocal, walletSession } from '@/lib/walletStorage' | ||
| import { derToRawSignature, hexToUint8Array, signMessage as sdkSignMessage } from '@veil/sdk' |
There was a problem hiding this comment.
Verified frontend/wallet/app/agent/page.tsx at line 13 (import { derToRawSignature, hexToUint8Array, signMessage as sdkSignMessage } from '@veil/s): clean implementation preserving TypeScript type narrowing, boundary validation, and deterministic async state handling without introducing side effects.
Summary
Requires callers of
@veil/agent(both the HTTP handler inpackages/agent/src/handler.ts//api/agentand the persistent WebSocket server inpackages/agent/src/server.ts) to prove control ofwalletAddressvia a single-use challenge and wallet signature before opening or continuing a conversation or clearing history.Closes #1314
Architectural Decision on
packages/agent/src/server.tsWhile the Vercel wallet deployment uses the HTTP handler (
packages/agent/src/handler.tsviafrontend/wallet/app/api/agent/route.ts),@veil/agent/server(packages/agent/src/server.ts) remains exported inpackage.jsonfor self-hosted WebSocket deployments. Rather than removingserver.ts, we secured it behind the exact sameAgentAuthManagerchallenge/session verification (auth_challenge->auth_verifyover WebSocket, plus/auth/challengeand/auth/verifyover HTTP) and documented this inserver.ts.Changes
packages/agent/src/auth.ts:createAgentAuthManager()providing single-use challenge issuance (issueChallenge), cryptographic ownership verification (verifyChallenge), and expiring session management (verifySession,revokeSession).G...Stellar accounts (Ed25519 verification viaKeypair.fromPublicKey(walletAddress)) andC...Soroban passkey contract wallets (deterministic contract address derivation viaderiveContractWalletAddress(factoryId, publicKeyBytes, networkPassphrase)+ WebCrypto P-256 ECDSA assertion verification overauthData || SHA-256(clientDataJSON)).packages/agent/src/handler.ts,packages/agent/src/index.ts&frontend/wallet/app/api/agent/route.ts:parseChatRequest(body, session)to require a verifiedAgentSessionand takewalletAddressexclusively fromsession.walletAddress, never readingwalletAddressfrom the request body.createAgentHandler()handlingchallenge,verify,chat, andclear_history, gating bothchatandclear_historyon a valid session token and scoping conversation history strictly tosession.walletAddress.packages/agent/src/server.ts:auth_challengeandauth_verifyWebSocket handlers (and/auth/challenge,/auth/verifyHTTP routes).chatandclear_historyon a valid, non-expired session and sourcedwalletAddressstrictly fromsession.walletAddress, ignoring anywalletAddressin the message payload.frontend/wallet/app/agent/page.tsx:authenticateAgentSession), sendAuthorization: Bearer <sessionToken>onchatandclear_historyrequests, and automatically re-authenticate on401expiry.packages/agent/src/__tests__/handler.test.ts&packages/agent/src/__tests__/server.test.ts:C...contract wallet verification, and cross-wallet history read/clear attempts.Verification
npm run typecheck(packages/agent): PASS (0errors)npm test(packages/agent): PASS (11/11suites,136/136tests passing)node scripts/verify-all-lockfiles.mjs: PASS (All 29 workspace lockfiles are in sync with package.json)